目前為止,Vue 為我們處理了所有的 DOM 更新,這要歸功於響應性和聲明式渲染。然而,有時我們也會不可避免地需要手動操作 DOM。
這時我們需要使用模板引用——也就是指向模板中一個 DOM 元素的 ref。我們需要通過這個特殊的 ref 屬性來實現模板引用:
<p ref="pElementRef">hello</p>
我們需要宣告一個同名的 ref:
const pElementRef = ref(null)
注意這個 ref 使用 null 值來初始化。這是因為當 script setup 執行時,DOM 元素還不存在。模板引用 ref 只能在組件掛載後訪問。
要在掛載之後執行代碼,我們可以使用 onMounted() 函數:
import { onMounted } from 'vue'
onMounted(() => {
// 此時組件已經掛載。
})
這被稱為生命週期鉤子——它允許我們註冊一個在組件的特定生命週期調用的回調函數。還有一些其他的鉤子如 onUpdated 和 onUnmounted。
嘗試添加一個 onMounted 鉤子,然後通過 pElementRef.value 訪問 p,並直接對其執行一些 DOM 操作。(例如修改它的 textContent)。
<script setup>
import { ref } from 'vue'
const pElementRef = ref(null)
</script>
<template>
<p ref="pElementRef">Hello</p>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const pElementRef = ref(null)
onMounted(() => {
pElementRef.value.textContent = 'mounted!'
})
</script>
<template>
<p ref="pElementRef">Hello</p>
</template>
有時我們需要響應性地執行一些“副作用”——例如,當一個數字改變時將其輸出到控制台。我們可以通過偵聽器來實現它:
import { ref, watch } from 'vue'
const count = ref(0)
watch(count, (newCount) => {
// 沒錯,console.log() 是一個副作用
console.log(`new count is: ${newCount}`)
})
watch() 可以直接偵聽一個 ref,並且只要 count 的值改變就會觸發回調。watch() 也可以偵聽其他類型的數據源
一個比在控制台輸出更加實際的例子是當 ID 改變時抓取新的數據。下面的例子中就是這樣一個組件。該組件被掛載時,會從模擬 API 中抓取 todo 數據,同時還有一個按鈕可以改變要抓取的 todo 的 ID。現在,嘗試實現一個偵聽器,使得組件能夠在按鈕被點擊時抓取新的 todo 項目。
<script setup>
import { ref } from 'vue'
const todoId = ref(1)
const todoData = ref(null)
async function fetchData() {
todoData.value = null
const res = await fetch(
`https://jsonplaceholder.typicode.com/todos/${todoId.value}`
)
todoData.value = await res.json()
}
fetchData()
</script>
<template>
<p>Todo id: {{ todoId }}</p>
<button @click="todoId++" :disabled="!todoData">Fetch next todo</button>
<p v-if="!todoData">Loading...</p>
<pre v-else>{{ todoData }}</pre>
</template>
<script setup>
import { ref, watch } from 'vue'
const todoId = ref(1)
const todoData = ref(null)
async function fetchData() {
todoData.value = null
const res = await fetch(
`https://jsonplaceholder.typicode.com/todos/${todoId.value}`
)
todoData.value = await res.json()
}
fetchData()
watch(todoId, fetchData)
</script>
<template>
<p>Todo id: {{ todoId }}</p>
<button @click="todoId++" :disabled="!todoData">Fetch next todo</button>
<p v-if="!todoData">Loading...</p>
<pre v-else>{{ todoData }}</pre>
</template>